import os
import shutil
import hashlib
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from datetime import datetime
import configparser

class FileReplacerApp:
    def __init__(self, root):
        self.root = root
        # 读取配置文件
        self.config_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.ini")

        # 默认路径设置
        self.source_base_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "UserData")
        self.target_path = r"C:\Users\YouXiDongXi\AppData\Local\Sony Corporation\PS Remote Play"

        # 加载或创建配置
        self.load_config()

        self.root.title("存档替换工具")

        # 设置窗口大小和最小尺寸
        self.root.geometry(self.window_geometry)
        self.root.minsize(300, 225)

        # 设置窗口大小变化的回调
        self.root.bind('<Configure>', self.on_window_configure)
        self.last_geometry_save = datetime.now()

        # 确保源文件夹存在
        self.ensure_source_folder_exists()

        # 目标路径状态
        self.target_path_valid = True

        self.selected_archive = None
        self.create_widgets()
        self.refresh_archive_list()

    def load_config(self):
        """从配置文件加载所有设置"""
        config = configparser.ConfigParser()
        self.window_geometry = "400x300"  # 默认窗口大小

        if os.path.exists(self.config_file):
            try:
                config.read(self.config_file, encoding='utf-8')
                if 'Window' in config and 'geometry' in config['Window']:
                    self.window_geometry = config['Window']['geometry']
                if 'Paths' in config:
                    if 'source_path' in config['Paths']:
                        self.source_base_path = config['Paths']['source_path']
                    if 'target_path' in config['Paths']:
                        self.target_path = config['Paths']['target_path']
            except Exception as e:
                self.log_message(f"读取配置文件时出错: {str(e)}\n")
        else:
            # 如果配置文件不存在，创建默认配置
            self.create_default_config()

    def create_default_config(self):
        """创建默认配置文件"""
        # 确保源文件夹存在
        self.ensure_source_folder_exists()

        # 弹窗让用户选择目标路径
        target_path_selected = filedialog.askdirectory(
            initialdir=r"C:\Users",
            title="请选择PS Remote Play目标文件夹"
        )

        # 如果用户取消选择，使用默认路径
        if not target_path_selected:
            self.target_path = r"C:\Users\YouXiDongXi\AppData\Local\Sony Corporation\PS Remote Play"
        else:
            self.target_path = target_path_selected

        # 保存默认配置
        self.save_config()

    def load_path_config(self):
        """仅加载路径配置（保持原有逻辑）"""
        config = configparser.ConfigParser()
        if os.path.exists(self.config_file):
            try:
                config.read(self.config_file, encoding='utf-8')
                if 'Paths' in config:
                    if 'source_path' in config['Paths']:
                        self.source_base_path = config['Paths']['source_path']
                    if 'target_path' in config['Paths']:
                        self.target_path = config['Paths']['target_path']
            except Exception as e:
                self.log_message(f"读取配置文件时出错: {str(e)}\n")

    def save_config(self):
        """保存所有设置到配置文件"""
        config = configparser.ConfigParser()
        config['Window'] = {
            'geometry': self.root.geometry()
        }
        config['Paths'] = {
            'source_path': self.source_base_path,
            'target_path': self.target_path
        }
        try:
            with open(self.config_file, 'w', encoding='utf-8') as configfile:
                config.write(configfile)
        except Exception as e:
            self.log_message(f"保存配置文件时出错: {str(e)}\n")

    def on_window_configure(self, event):
        """窗口配置变化事件处理"""
        # 只处理根窗口的配置变化事件
        if event.widget == self.root:
            # 每10秒保存一次窗口大小
            current_time = datetime.now()
            if (current_time - self.last_geometry_save).seconds >= 10:
                self.last_geometry_save = current_time
                self.save_config()

    def ensure_source_folder_exists(self):
        """确保源文件夹存在，如果不存在则创建"""
        if not os.path.exists(self.source_base_path):
            try:
                os.makedirs(self.source_base_path)
                print(f"已创建源文件夹: {self.source_base_path}")
            except Exception as e:
                messagebox.showerror("错误", f"无法创建源文件夹 {self.source_base_path}: {str(e)}")

    def check_target_path(self):
        """检查目标路径是否存在，如果不存在则提示用户"""
        if not os.path.exists(self.target_path):
            self.target_path_valid = False
            # 标红目标路径输入框
            self.target_path_entry.config(style='Error.TEntry')
            # 弹窗提示
            messagebox.showwarning("路径不存在",
                                   f"目标路径不存在:\n{self.target_path}\n\n请在菜单栏<文件>中修改目标文件夹路径。")
            self.log_message(f"警告: 目标路径不存在 {self.target_path}\n")
            return False
        else:
            self.target_path_valid = True
            # 恢复正常样式
            self.target_path_entry.config(style='TEntry')
            return True

    def create_widgets(self):
        # 创建自定义样式
        style = ttk.Style()
        style.configure('Error.TEntry', fieldbackground='red', foreground='white')
        # 创建菜单栏
        self.create_menu()

        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)

        # 配置main_frame的网格权重以支持缩放
        main_frame.columnconfigure(0, weight=1)  # 存档列表列
        main_frame.columnconfigure(1, weight=1)  # 操作按钮列
        main_frame.rowconfigure(0, weight=0)     # 路径信息区域（固定高度）
        main_frame.rowconfigure(1, weight=1)     # 存档列表区域（可缩放）
        main_frame.rowconfigure(2, weight=2)     # 输出文本区域（更多缩放权重）

        # 源文件夹和目标文件夹显示
        path_frame = ttk.LabelFrame(main_frame, text="路径信息", padding="10")
        path_frame.grid(row=0, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
        path_frame.columnconfigure(1, weight=1)

        # 在path_frame创建后添加样式定义
        self.normal_style = {'state': 'readonly'}
        self.error_style = {'state': 'readonly', 'style': 'Error.TEntry'}

        ttk.Label(path_frame, text="源文件夹:").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
        self.source_path_var = tk.StringVar(value=self.source_base_path)
        ttk.Entry(path_frame, textvariable=self.source_path_var, state="readonly").grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 5))
        ttk.Button(path_frame, text="打开", command=self.open_source_folder).grid(row=0, column=2)

        ttk.Label(path_frame, text="目标文件夹:").grid(row=1, column=0, sticky=tk.W, padx=(0, 5), pady=(5, 0))
        self.target_path_var = tk.StringVar(value=self.target_path)
        self.target_path_entry = ttk.Entry(path_frame, textvariable=self.target_path_var, **self.normal_style)
        self.target_path_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(0, 5), pady=(5, 0))
        ttk.Button(path_frame, text="打开", command=self.open_target_folder).grid(row=1, column=2, pady=(5, 0))

        # 存档列表
        list_frame = ttk.LabelFrame(main_frame, text="存档列表", padding="10")
        list_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
        list_frame.rowconfigure(0, weight=1)

        self.archive_listbox = tk.Listbox(list_frame)
        self.archive_listbox.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        self.archive_listbox.bind('<<ListboxSelect>>', self.on_archive_select)

        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.archive_listbox.yview)
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        self.archive_listbox.configure(yscrollcommand=scrollbar.set)

        # 操作按钮
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=1, column=1, padx=(10, 0), sticky=(tk.N, tk.S))
        button_frame.rowconfigure(1, weight=1)

        ttk.Button(button_frame, text="刷新列表", command=self.refresh_archive_list).grid(row=0, column=0, pady=(0, 10))
        self.replace_button = ttk.Button(button_frame, text="替换存档", command=self.replace_files, state="disabled")
        self.replace_button.grid(row=1, column=0, pady=(0, 10))

        # 输出文本框
        output_frame = ttk.LabelFrame(main_frame, text="替换过程", padding="10")
        output_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
        output_frame.columnconfigure(0, weight=1)
        output_frame.rowconfigure(0, weight=1)

        self.output_text = tk.Text(output_frame, wrap=tk.WORD)
        self.output_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        output_scrollbar = ttk.Scrollbar(output_frame, orient=tk.VERTICAL, command=self.output_text.yview)
        output_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        self.output_text.configure(yscrollcommand=output_scrollbar.set)

    def create_menu(self):
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label="修改源文件夹路径", command=self.change_source_path)
        file_menu.add_command(label="修改目标文件夹路径", command=self.change_target_path)
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.root.quit)

    def get_timestamp(self):
        """获取当前时间戳"""
        return datetime.now().strftime("[%Y-%m-%d %H:%M:%S] ")

    def log_message(self, message):
        """带时间戳的日志输出"""
        timestamped_message = self.get_timestamp() + message
        self.output_text.insert(tk.END, timestamped_message)
        self.output_text.see(tk.END)
        self.root.update_idletasks()

    def change_source_path(self):
        """修改源文件夹路径"""
        new_path = filedialog.askdirectory(initialdir=self.source_base_path, title="选择源文件夹")
        if new_path:
            self.source_base_path = new_path
            self.source_path_var.set(new_path)
            self.save_config()  # 保存到配置文件
            self.refresh_archive_list()
            self.log_message(f"源文件夹已更改为: {new_path}\n")

    def change_target_path(self):
        new_path = filedialog.askdirectory(initialdir=self.target_path, title="选择目标文件夹")
        if new_path:
            self.target_path = new_path
            self.target_path_var.set(new_path)
            self.save_config()  # 保存到配置文件
            # 检查新路径并更新样式
            self.check_target_path()
            self.log_message(f"目标文件夹已更改为: {new_path}\n")

    def refresh_archive_list(self):
        self.archive_listbox.delete(0, tk.END)
        if not os.path.exists(self.source_base_path):
            self.log_message(f"源文件夹不存在: {self.source_base_path}\n")
            self.log_message(f"点击菜单栏<文件>选项修改源文件夹路径\n")
            return

        # 检查目标路径
        self.check_target_path()

        try:
            archives = [d for d in os.listdir(self.source_base_path)
                        if os.path.isdir(os.path.join(self.source_base_path, d))]
            for archive in archives:
                self.archive_listbox.insert(tk.END, archive)
            self.log_message(f"已找到 {len(archives)} 个存档\n")
        except Exception as e:
            self.log_message(f"读取存档列表时出错: {str(e)}\n")

    def on_archive_select(self, event):
        selection = self.archive_listbox.curselection()
        if selection:
            self.selected_archive = self.archive_listbox.get(selection[0])
            self.replace_button.config(state="normal")
            self.log_message(f"已选择存档: {self.selected_archive}\n")
        else:
            self.selected_archive = None
            self.replace_button.config(state="disabled")

    def open_source_folder(self):
        if os.path.exists(self.source_base_path):
            os.startfile(self.source_base_path)
        else:
            messagebox.showerror("错误", "源文件夹不存在")

    def open_target_folder(self):
        if os.path.exists(self.target_path):
            os.startfile(self.target_path)
        else:
            messagebox.showerror("错误", "目标文件夹不存在")

    def calculate_file_hash(self, file_path):
        """计算文件的MD5哈希值"""
        hash_md5 = hashlib.md5()
        try:
            with open(file_path, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    hash_md5.update(chunk)
            return hash_md5.hexdigest()
        except Exception as e:
            self.log_message(f"计算文件哈希时出错 {file_path}: {str(e)}\n")
            return None

    def replace_files(self):
        if not self.selected_archive:
            messagebox.showwarning("警告", "请先选择一个存档")
            return

        source_archive_path = os.path.join(self.source_base_path, self.selected_archive)
        if not os.path.exists(source_archive_path):
            self.log_message(f"存档路径不存在: {source_archive_path}\n")
            return

        if not os.path.exists(self.target_path):
            self.log_message(f"目标路径不存在: {self.target_path}\n")
            return

        self.log_message(f"="*30+"\n")
        self.log_message(f"开始替换存档: {self.selected_archive}\n")
        self.log_message(f"从: {source_archive_path}\n")
        self.log_message(f"到: {self.target_path}\n")

        success_count = 0
        total_count = 0

        try:
            for root, dirs, files in os.walk(source_archive_path):
                for file in files:
                    total_count += 1
                    source_file_path = os.path.join(root, file)
                    relative_path = os.path.relpath(source_file_path, source_archive_path)
                    target_file_path = os.path.join(self.target_path, relative_path)

                    # 创建目标文件夹
                    target_dir = os.path.dirname(target_file_path)
                    if not os.path.exists(target_dir):
                        os.makedirs(target_dir)

                    # 计算源文件哈希
                    source_hash = self.calculate_file_hash(source_file_path)
                    if source_hash is None:
                        self.log_message(f"跳过文件 (无法读取): {relative_path}\n")
                        continue

                    # 如果目标文件存在，计算其哈希
                    if os.path.exists(target_file_path):
                        target_hash = self.calculate_file_hash(target_file_path)
                        if target_hash is None:
                            self.log_message(f"跳过文件 (无法读取目标文件): {relative_path}\n")
                            continue

                        if source_hash == target_hash:
                            self.log_message(f"跳过文件 (内容相同): {relative_path}\n")
                            success_count += 1
                            continue

                    # 复制文件
                    try:
                        shutil.copy2(source_file_path, target_file_path)
                        # 验证复制后的文件
                        new_target_hash = self.calculate_file_hash(target_file_path)
                        if new_target_hash == source_hash:
                            self.log_message(f"成功替换: {relative_path}\n")
                            success_count += 1
                        else:
                            self.log_message(f"替换失败 (哈希验证失败): {relative_path}\n")
                    except Exception as e:
                        self.log_message(f"替换失败: {relative_path} - {str(e)}\n")

            # 完成报告
            self.log_message(f"\n替换完成! 成功: {success_count}/{total_count} 个文件\n")
            if success_count == total_count:
                self.log_message("所有文件替换成功!\n")
            else:
                self.log_message(f"有 {total_count - success_count} 个文件替换失败\n")

        except Exception as e:
            self.log_message(f"\n替换过程中发生错误: {str(e)}\n")

def main():
    root = tk.Tk()
    app = FileReplacerApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()
